// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#pragma once

#include <experimental/filesystem>
#include <string>

#include <span.hpp>

#include "coin2/strategy/strategy.h"
#include "presto/quant/math/moving_average.h"
#include "presto/quant/math/moving_window.h"

using coin2::exchange::base::feed::FeedUpdate;
using presto::math::TimeWindowMovingAverage;

enum Feature {
  TRADE_UNSIGNED_QTY_10MS,
  TRADE_UNSIGNED_QTY_20MS,
  TRADE_UNSIGNED_QTY_30MS,
  TRADE_SIGNED_QTY_10MS,
  TRADE_SIGNED_QTY_20MS,
  TRADE_SIGNED_QTY_30MS,
  TRADE_UNSIGNED_CNT_10MS,
  TRADE_UNSIGNED_CNT_20MS,
  TRADE_UNSIGNED_CNT_30MS,
  TRADE_SIGNED_CNT_10MS,
  TRADE_SIGNED_CNT_20MS,
  TRADE_SIGNED_CNT_30MS,

  NUM_FEATURE,
};

static_assert(Feature::NUM_FEATURE == 12);

class OrderLatencyFeatures : public coin2::strategy::IStrategy {
 public:
  explicit OrderLatencyFeatures(const std::string& target_symbol)
      : target_symbol_(target_symbol), trade_unsigned_qty_10ms_() {
    trade_unsigned_qty_10ms_.ResizeWindow(10 * MS);
    trade_unsigned_qty_20ms_.ResizeWindow(20 * MS);
    trade_unsigned_qty_30ms_.ResizeWindow(30 * MS);

    trade_signed_qty_10ms_.ResizeWindow(10 * MS);
    trade_signed_qty_20ms_.ResizeWindow(20 * MS);
    trade_signed_qty_30ms_.ResizeWindow(30 * MS);

    trade_signed_cnt_10ms_.ResizeWindow(10 * MS);
    trade_signed_cnt_20ms_.ResizeWindow(20 * MS);
    trade_signed_cnt_30ms_.ResizeWindow(30 * MS);
  }

  void onTradeFeed(const FeedUpdate& upd) override {
    if (upd.product().absolute_norm() != target_symbol_) {
      printf("%s != %s\n", upd.product().absolute_norm().c_str(), target_symbol_.c_str());
      return;
    }

    // update x
    auto&& ts = upd.timestamp();
    auto& trade = upd.trade();
    trade_unsigned_qty_10ms_.Update(ts, trade.fill_qty);
    trade_unsigned_qty_20ms_.Update(ts, trade.fill_qty);
    trade_unsigned_qty_30ms_.Update(ts, trade.fill_qty);

    const auto signed_qty = trade.signed_qty();
    trade_signed_qty_10ms_.Update(ts, signed_qty);
    trade_signed_qty_20ms_.Update(ts, signed_qty);
    trade_signed_qty_30ms_.Update(ts, signed_qty);

    const auto sign = trade.sign();
    trade_signed_cnt_10ms_.Update(ts, sign);
    trade_signed_cnt_20ms_.Update(ts, sign);
    trade_signed_cnt_30ms_.Update(ts, sign);
  }

  void GetX(nonstd::span<double, Feature::NUM_FEATURE> out) {
    out[0] = trade_unsigned_qty_10ms_.average();
    out[1] = trade_unsigned_qty_20ms_.average();
    out[2] = trade_unsigned_qty_30ms_.average();

    out[3] = trade_signed_qty_10ms_.average();
    out[4] = trade_signed_qty_20ms_.average();
    out[5] = trade_signed_qty_30ms_.average();

    out[6] = trade_signed_qty_10ms_.num_elements();
    out[7] = trade_signed_qty_20ms_.num_elements();
    out[8] = trade_signed_qty_30ms_.num_elements();

    out[9] = trade_signed_cnt_10ms_.average();
    out[10] = trade_signed_cnt_20ms_.average();
    out[11] = trade_signed_cnt_30ms_.average();
  }

 private:
  std::string target_symbol_;

  static constexpr const int64_t MS = 1'000'000;
  // x features
  TimeWindowMovingAverage<double> trade_unsigned_qty_10ms_;
  TimeWindowMovingAverage<double> trade_unsigned_qty_20ms_;
  TimeWindowMovingAverage<double> trade_unsigned_qty_30ms_;
  TimeWindowMovingAverage<double> trade_signed_qty_10ms_;
  TimeWindowMovingAverage<double> trade_signed_qty_20ms_;
  TimeWindowMovingAverage<double> trade_signed_qty_30ms_;
  TimeWindowMovingAverage<double> trade_signed_cnt_10ms_;
  TimeWindowMovingAverage<double> trade_signed_cnt_20ms_;
  TimeWindowMovingAverage<double> trade_signed_cnt_30ms_;
};
